#include <ctime>
#include <memory>
#include <random>

#include "VectorTests.h"
#include "../TestFramework/TestVector.h"

namespace
{
    template <class T>
    class TestProcessor : public TestFramework::VectorProcessor<T>
    {
    public:
        virtual void ProcessItem(T& item)
        {
            item += item;
        }
    };

    template <class T>
    class TestComparer : public TestFramework::VectorComparer<T>
    {
    public:
        virtual bool Compare(const T& lhs, const T& rhs) const
        {
            return lhs > rhs;
        }
    };

    class TestComposite
    {
    public:
        int TestInt;
        std::string TestString;
        std::auto_ptr<TestComposite> TestPtr;
    };

    TEST_CASE(TestVectorCreation)
    {
        TestFramework::TestVector<long> vector(19);
        CHECK(vector.GetSize() == 0);
    }

    TEST_CASE(TestVectorFilling)
    {
        TestFramework::TestVector<long> vector;
        for (long i = 0; i < 123; ++i)
        {
            vector.PushBack(i);
        }

        CHECK(vector.GetSize() == 123);
    }

    TEST_CASE(TestVectorCompositeFilling)
    {
        TestFramework::TestVector<TestComposite> vector(5);
        TestComposite item;
        for (int i = 0; i < 5; ++i)
        {
            vector.PushBack(item);
        }

        CHECK(vector.GetSize() == 5);
    }

    TEST_CASE(TestVectorCheckOutOfRange)
    {
        TestFramework::TestVector<int> vector(7);
        CHECK_EXCEPTION_OCCURRENCE(vector[10] = 10);
    }

    TEST_CASE(TestVectorInserting)
    {
        TestFramework::TestVector<int> vector(0);
        vector.Insert(vector.Begin(), 2);
        vector.Insert(vector.Begin(), 0);
        vector.Insert(vector.Begin() + 1, 1);
        vector.Insert(vector.Begin() + 3, 3);

        for (int i = 0; i < 4; ++i)
        {
            CHECK(vector[i] == i);
        }
    }

    TEST_CASE(TestVectorClearing)
    {
        TestFramework::TestVector<long> vector;
        for (long i = 0; i < 123; ++i)
        {
            vector.PushBack(i);
        }

        vector.Clear();
        CHECK(vector.GetSize() == 0);
    }

    TEST_CASE(TestVectorItemsRemoving)
    {
        TestFramework::TestVector<int> vector(4);
        for (int i = 0; i < 4; ++i)
        {
            vector.PushBack(i);
        }

        vector.PushBack(2);

        vector.Remove(2);
        vector.Remove(3);
        vector.Remove(0);

        CHECK(vector[0] == 1 && vector.GetSize() == 1);
    }

    TEST_CASE(TestVectorRemoveItemsAt)
    {
        TestFramework::TestVector<int> vector(4);
        for (int i = 0; i < 4; ++i)
        {
            vector.PushBack(i);
        }

        vector.RemoveAt(2);
        vector.RemoveAt(2);
        vector.Remove(0);

        CHECK(vector[0] == 1 && vector.GetSize() == 1);
    }

    TEST_CASE(TestVectorEnumerate)
    {
        TestFramework::TestVector<int> vector(99);
        for (int i = 0; i < 99; ++i)
        {
            vector.PushBack(i);
        }

        TestProcessor<int> processor;
        vector.Enumerate(processor);

        for (int i = 0; i < 99; ++i)
        {
            CHECK(vector[i] == (i + i));
        }
    }

    TEST_CASE(TestVectorComparing)
    {
        typedef TestFramework::TestVector<int> Type;
        Type mass(30);
        std::srand(unsigned(std::time(0)));
        for (int i=0; i < 30; ++i)
        {
            mass[i] = std::rand() % 9;
        }

        TestComparer<Type::ValueType> comparer;
        mass.Sort(comparer);

        for (int i=0; i < 29; ++i)
        {
            CHECK(mass[i] <= mass[i + 1]);
        }
    }

    TEST_CASE(TestVectorIsEmpty)
    {
        typedef TestFramework::TestVector<int> Type;
        Type vec1;
        CHECK(vec1.IsEmpty());
        Type vec2(0);
        CHECK(vec2.IsEmpty());
        Type vec3(10);
        CHECK(vec3.IsEmpty());
        vec1.PushBack(2);
        CHECK(!vec1.IsEmpty());
        vec2.PushBack(3);
        CHECK(!vec2.IsEmpty());
        vec3.PushBack(4);
        CHECK(!vec3.IsEmpty());
        vec3.Clear();
        CHECK(vec3.IsEmpty());
        vec3[0] = 10;
        CHECK(!vec3.IsEmpty());
    }

    START_TEST_SUITE(VectorTest, "Vector test")
    {
        REGISTER_TEST(TestVectorCreation);
        REGISTER_TEST(TestVectorFilling);
        REGISTER_TEST(TestVectorCompositeFilling);
        REGISTER_TEST(TestVectorCheckOutOfRange);
        REGISTER_TEST(TestVectorInserting);
        REGISTER_TEST(TestVectorClearing);
        REGISTER_TEST(TestVectorItemsRemoving);
        REGISTER_TEST(TestVectorRemoveItemsAt);
        REGISTER_TEST(TestVectorEnumerate);
        REGISTER_TEST(TestVectorComparing);
        REGISTER_TEST(TestVectorIsEmpty);
    }
    FINISH_TEST_SUITE
}

namespace Tests
{
    TestFramework::UnitTestPtr CreateVectorTester()
    {
        CREATE_SUITE(VectorTest)
    }
}
